package com.maple.p2pclient.socket;


import com.maple.p2pclient.utils.LogUtils;
import com.maple.p2pclient.utils.SSLSocketClient;
import com.maple.p2pclient.utils.TrustAllManager;

import org.json.JSONException;
import org.json.JSONObject;
import org.webrtc.IceCandidate;
import org.webrtc.SessionDescription;

import java.security.SecureRandom;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;

import io.socket.client.IO;
import io.socket.client.Socket;
import io.socket.emitter.Emitter;
import io.socket.engineio.client.EngineIOException;
import okhttp3.OkHttpClient;

public class MySocketIOClient {

    private Socket socket;

    private Callback callback;

    private String token;

    public void setToken(String token) {
        this.token = token;
    }


    public void setCallback(Callback call) {
        this.callback = call;
    }



    public void connect(String url) {
        try {
            SSLContext sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, new TrustManager[]{new TrustAllManager()}, new SecureRandom());
            SSLSocketFactory sslSocketFactory = SSLSocketClient.createSSLSocketFactory();

            HostnameVerifier hostnameVerifier = (hostname, session) -> true;
            OkHttpClient okHttpClient = new OkHttpClient.Builder()
                    .sslSocketFactory(sslSocketFactory, new TrustAllManager())
                    .hostnameVerifier(hostnameVerifier)
                    .build();

            IO.Options options = new IO.Options();
            options.query = "token=" + token;
            IO.setDefaultOkHttpWebSocketFactory(okHttpClient);
            IO.setDefaultOkHttpCallFactory(okHttpClient);
            socket = IO.socket(url,options);
        } catch (Exception e) {
            e.printStackTrace();
            if(callback != null) {
                callback.onException("socket ERROR:" + e.getMessage());
            }
            LogUtils.logGGQ("socket ERROR：" + e.getMessage());
        }

        if(socket != null) {
            socket.on(Socket.EVENT_CONNECT, onConnect);
            socket.on(Socket.EVENT_DISCONNECT, onDisconnect);
            socket.on(Socket.EVENT_CONNECT_ERROR, onConnectError);

            socket.on("offer", new Emitter.Listener() {
                @Override
                public void call(Object... args) {
                    try {
                        JSONObject json = (JSONObject) args[0];
                        if(callback != null) {
                            callback.onOffer(json);
                        }
                    }catch (Exception e) {
                        if(callback != null) {
                            callback.onException("offer ERROR:" + e.getMessage());
                        }
                        LogUtils.logGGQ("offer ERROR:" + e.getMessage());
                    }
                }
            });
            socket.on("answer", new Emitter.Listener() {
                @Override
                public void call(Object... args) {
                    try {
                        JSONObject json = (JSONObject) args[0];
                        if(callback != null) {
                            callback.onAnswer(json);
                        }
                    }catch (Exception e) {
                        if(callback != null) {
                            callback.onException("answer ERROR:" + e.getMessage());
                        }
                        LogUtils.logGGQ("answer ERROR:" + e.getMessage());
                    }
                }
            });

            socket.on("iceCandidate", new Emitter.Listener() {
                @Override
                public void call(Object... args) {
                    try {
                        JSONObject json = (JSONObject) args[0];
                        if(callback != null) {
                            callback.onIceCandidate(json);
                        }
                    } catch (Exception e) {
                        if(callback != null) {
                            callback.onException("iceCandidate ERROR:" + e.getMessage());
                        }
                        LogUtils.logGGQ("iceCandidate ERROR:" + e.getMessage());
                    }
                }
            });
            socket.on("event", new Emitter.Listener() {
                @Override
                public void call(Object... args) {
                    try {
                        JSONObject json = (JSONObject) args[0];
                        if(callback != null) {
                            callback.onEvent(json);
                        }
                    } catch (Exception e) {
                        if(callback != null) {
                            callback.onException("event ERROR:" + e.getMessage());
                        }
                        LogUtils.logGGQ("event ERROR:" + e.getMessage());
                    }
                }
            });
            socket.connect();
        }

    }

    private Emitter.Listener onConnect = new Emitter.Listener() {
        @Override
        public void call(Object... args) {
            LogUtils.logGGQ("socket onConnect");
            if(callback != null) {
                callback.onConnected();
            }
        }
    };


    private Emitter.Listener onDisconnect = new Emitter.Listener() {
        @Override
        public void call(Object... args) {
            LogUtils.logGGQ("socket onDisconnect");
            if(callback != null) {
                callback.onDisconnect();
            }
        }
    };

    private Emitter.Listener onConnectError = new Emitter.Listener() {
        @Override
        public void call(Object... args) {
            LogUtils.logGGQ("socket onConnectError");
            Object obj = args[0];
            if(obj instanceof EngineIOException) {
                LogUtils.logGGQ("连接失败：" + obj);
                if(callback != null) {
                    callback.onConnectError(((EngineIOException) obj).getMessage());
                }
            }
        }
    };

    public void sendEvent(String event, JSONObject json) {
        if(isConnected()) {
            socket.emit(event,json);
        }
    }

    public void sendOffer(SocketType form,SocketType to, String user,SessionDescription sdp) {
        if(isConnected()) {
            try {
                JSONObject json = new JSONObject();
                json.put("form",form.getType());
                json.put("to",to.getType());
                json.put("type",sdp.type);
                json.put("sdp",sdp.description);
                json.put("user",user);
                socket.emit("offer",json);
            } catch (JSONException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public void sendAnswer(SocketType form,SocketType to, String user,SessionDescription sdp) {
        if(isConnected()) {
            try {
                JSONObject json = new JSONObject();
                json.put("form",form.getType());
                json.put("to",to.getType());
                json.put("type",sdp.type);
                json.put("sdp",sdp.description);
                json.put("user",user);
                socket.emit("answer",json);
            } catch (JSONException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public void sendIceCandidate(SocketType form,SocketType to, String user,IceCandidate iceCandidate) {
        if(isConnected()) {
            try {
                JSONObject json = new JSONObject();
                json.put("form",form.getType());
                json.put("to",to.getType());
                json.put("sdpMLineIndex",iceCandidate.sdpMLineIndex);
                json.put("sdpMid",iceCandidate.sdpMid);
                json.put("candidate",iceCandidate.sdp);
                json.put("user",user);
                socket.emit("iceCandidate",json);
            } catch (JSONException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public void close() {
        if(socket != null) {
            socket.close();
        }
    }

    public boolean isConnected() {
        if(socket != null && socket.connected()) {
            return true;
        }
        return false;
    }

    public interface Callback {
        void onConnected();
        void onDisconnect();
        void onConnectError(String error);

        void onOffer(JSONObject json);
        void onAnswer(JSONObject json);
        void onIceCandidate(JSONObject json);

        void onException(String error);

        void onEvent(JSONObject json);
    }
}
